#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#pragma pack(push, 1) // 确保结构体按字节对齐
typedef struct {
    unsigned short bfType;      // 文件类型
    unsigned int bfSize;        // 文件大小
    unsigned short bfReserved1; // 保留字段
    unsigned short bfReserved2; // 保留字段
    unsigned int bfOffBits;     // 从文件头到像素数据的偏移量
} BITMAPFILEHEADER;

typedef struct {
    unsigned int biSize;          // 结构体大小
    int biWidth;                  // 图像宽度
    int biHeight;                 // 图像高度
    unsigned short biPlanes;      // 颜色平面数
    unsigned short biBitCount;    // 每个像素的位数
    unsigned int biCompression;    // 压缩类型
    unsigned int biSizeImage;     // 图像大小
    int biXPelsPerMeter;          // 水平分辨率
    int biYPelsPerMeter;          // 垂直分辨率
    unsigned int biClrUsed;       // 使用的颜色数
    unsigned int biClrImportant;  // 重要的颜色数
} BITMAPINFOHEADER;
#pragma pack(pop)

/**
 * 生成BMP图像的灰度直方图
 * @param filename BMP文件路径
 * @param scale_factor 缩放因子，用于控制输出柱状图的大小
 * @param output_file 输出文件，如果为NULL则输出到控制台
 */
void generate_histogram(const char *filename, int scale_factor, const char *output_file) {
    FILE *file = fopen(filename, "rb");
    if (!file) {
        fprintf(stderr, "错误: 无法打开文件 '%s'\n", filename);
        return;
    }

    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER infoHeader;

    // 读取 BMP 文件头
    fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, file);
    fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, file);

    // 检查文件类型和有效性
    if (fileHeader.bfType != 0x4D42) { // 'BM'
        fprintf(stderr, "错误: 不是有效的BMP文件\n");
        fclose(file);
        return;
    }
    
    if (infoHeader.biBitCount != 24) {
        fprintf(stderr, "错误: 仅支持24位BMP文件\n");
        fclose(file);
        return;
    }

    // 计算图像数据的偏移量
    fseek(file, fileHeader.bfOffBits, SEEK_SET);

    int width = infoHeader.biWidth;
    int height = abs(infoHeader.biHeight); // 处理负高度的情况
    int histogram[256] = {0}; // 存储每个灰度值的计数
    
    // 计算行对齐
    int row_padding = (4 - (width * 3) % 4) % 4;
    
    printf("正在处理图像: %dx%d 像素\n", width, height);
    
    // 读取像素数据并计算灰度直方图
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            unsigned char pixel[3]; // BGR (BMP格式)
            if (fread(pixel, sizeof(unsigned char), 3, file) != 3) {
                fprintf(stderr, "错误: 读取像素数据失败\n");
                fclose(file);
                return;
            }
            // 将 BGR 转换为灰度（使用标准加权平均）
            unsigned char gray = (unsigned char)(0.299 * pixel[2] + 0.587 * pixel[1] + 0.114 * pixel[0]);
            histogram[gray]++;
        }
        // 跳过行填充字节
        fseek(file, row_padding, SEEK_CUR);
    }

    fclose(file);
    
    // 找到最大值用于缩放
    int max_count = 0;
    int total_pixels = width * height;
    for (int i = 0; i < 256; i++) {
        if (histogram[i] > max_count) {
            max_count = histogram[i];
        }
    }
    
    FILE *output = stdout;
    if (output_file) {
        output = fopen(output_file, "w");
        if (!output) {
            fprintf(stderr, "警告: 无法创建输出文件，使用控制台输出\n");
            output = stdout;
        }
    }
    
    fprintf(output, "\n=== 灰度直方图分析 ===\n");
    fprintf(output, "图像尺寸: %dx%d\n", width, height);
    fprintf(output, "总像素数: %d\n", total_pixels);
    fprintf(output, "最大频次: %d\n\n", max_count);
    
    // 打印柱状图
    for (int i = 0; i < 256; i++) {
        if (histogram[i] > 0) { // 只显示有数据的灰度值
            double percentage = (double)histogram[i] / total_pixels * 100;
            int bar_length = (histogram[i] * scale_factor) / (max_count > 0 ? max_count : 1);
            
            fprintf(output, "%3d (%6.2f%%): ", i, percentage);
            for (int j = 0; j < bar_length; j++) {
                fprintf(output, "█");
            }
            fprintf(output, " (%d)\n", histogram[i]);
        }
    }
    
    if (output_file && output != stdout) {
        fclose(output);
        printf("直方图已保存到: %s\n", output_file);
    }
}

/**
 * 显示使用帮助信息
 */
void print_usage(const char *program_name) {
    printf("用法: %s <BMP文件> [选项]\n", program_name);
    printf("选项:\n");
    printf("  -s <数值>    设置柱状图缩放因子 (默认: 50)\n");
    printf("  -o <文件>    输出到指定文件\n");
    printf("  -h          显示此帮助信息\n");
    printf("\n示例:\n");
    printf("  %s image.bmp\n", program_name);
    printf("  %s image.bmp -s 30 -o histogram.txt\n", program_name);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        print_usage(argv[0]);
        return 1;
    }
    
    const char *filename = argv[1];
    int scale_factor = 50;
    const char *output_file = NULL;
    
    // 解析命令行参数
    for (int i = 2; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0) {
            print_usage(argv[0]);
            return 0;
        } else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
            scale_factor = atoi(argv[++i]);
            if (scale_factor <= 0) {
                fprintf(stderr, "错误: 缩放因子必须为正数\n");
                return 1;
            }
        } else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) {
            output_file = argv[++i];
        } else {
            fprintf(stderr, "错误: 未知参数 '%s'\n", argv[i]);
            print_usage(argv[0]);
            return 1;
        }
    }
    
    // 检查文件是否存在
    FILE *test_file = fopen(filename, "rb");
    if (!test_file) {
        fprintf(stderr, "错误: 文件 '%s' 不存在或无法访问\n", filename);
        return 1;
    }
    fclose(test_file);
    
    printf("开始分析文件: %s\n", filename);
    generate_histogram(filename, scale_factor, output_file);
    
    return 0;
} 